use parking_lot::Mutex;

#[cfg(feature = "avx")]
use superfeature_avx::SplitCollector;
#[cfg(feature = "avx")]
use crate::superfeature::RabinWindowAvx8;

use walkdir::WalkDir;
use pipelines::{Sender,Receiver};
use parking_lot::Condvar;
//use util::Cp;

use super::block::*;
use super::product::*;

use crate::backup::BackupError;
use crate::backup::BackupResult;
use crate::backup::BackupStatistics;
use crate::recipe::file::FileInfo;

use std::fs::metadata;
use std::sync::Arc;

pub struct ChunkReader {
	path:String,
	cond:Condvar,
	statistics:Arc<Mutex<BackupStatistics>>,
}

impl ChunkReader {
	pub fn new(path:String,statistics:Arc<Mutex<BackupStatistics>>) -> Self {
		Self {
			path:path,
			cond:Condvar::new(),
			statistics:statistics,
		}
	}

	pub fn read(&self) -> Result<Vec<Arc<Block>>,Box<dyn std::error::Error>> {
		let mut res = Vec::new();
		for entry in WalkDir::new(&self.path) {
			let entry = entry.unwrap();
			let meta = entry.metadata()?;
			if meta.is_dir() { continue; }
			let block = Arc::new(Block::new(
				entry.path().to_str().unwrap().to_string(),
				meta.len() as usize
			));
			res.push(block.clone());
			//block.read()?;
		}
		Ok(res)
	}

	pub fn read_async(&self,out:&Sender<ReaderProduct>) -> BackupResult<()> {
		let skip_path:bool = metadata(&self.path)
								.map_err(|e|BackupError::PathMetadata(self.path.clone(),e))?
								.is_dir();
		let mut cost_time:u128 = 0;
		let mut total_len:usize = 0;
		for entry in WalkDir::new(&self.path) {
			let entry = entry.map_err(|e|BackupError::Entry(e))?;
			let meta = std::fs::metadata(entry.path()).map_err(|e|BackupError::EntryMetadata(e))?;
			if meta.is_dir() { continue; }
			//file_total_len += meta.len();
			let path = entry.path();
			let backup_path = if skip_path {
				path.strip_prefix(&self.path)?.to_str().unwrap().to_string()
			} else {
				path.file_name().unwrap().to_str().unwrap().to_string()
			};
			let filename = path.to_str().unwrap().to_string();
			let file_len = meta.len() as usize;
			let block = Arc::new(Block::new(
				filename.clone(),
				file_len
			));
			//println!("backup_path {backup_path}");
			out.send(ReaderProduct::FileBlock(block.clone()));
			out.send(ReaderProduct::FileInfo(backup_path,meta.len()));
			total_len += file_len;
			cost_time += block.read(&self.cond)?;
		}
		let mut statistics = self.statistics.lock();
		statistics.set_read_phase_time(cost_time);
		statistics.set_origin_len(total_len);
		//println!("file total len {}",file_total_len);
		Ok(())
	}

	pub fn split_async(&self,out:&Sender<ChunkerProduct>,recv:Receiver<ReaderProduct>) -> BackupResult<()> {
		let mut chunk_counter = 0;
		let mut cost_time:u128 = 0;
		for b in recv {
			match b {
				ReaderProduct::FileInfo(filename,size) => {
					out.send(ChunkerProduct::FileInfo(FileInfo::new(chunk_counter,size,filename)));
					chunk_counter = 0;
				}
				ReaderProduct::FileBlock(b) => loop {
					match b.split(&self.cond) {
						(Some(cp),time) => {
							cost_time += time;
							chunk_counter += 1;
							out.send(ChunkerProduct::ChunkPointer(cp));
						}
						(None,_) => {
							break;
						}
					}
				}
			}
		}
		self.statistics.lock().set_split_phase_time(cost_time);
		Ok(())
	}
	
	
	#[cfg(feature = "avx")]
	pub fn split_rabin_avx8_async(&self,out:&Sender<ChunkerProduct>,recv:Receiver<ReaderProduct>) -> BackupResult<()> {
		let mut chunk_counter = 0;
		let mut cost_time:u128 = 0;
		let mut leiji = 0;
		let mut window = unsafe { RabinWindowAvx8::new() };
		let mut collector = SplitCollector::new(8, (0x10000-48)/8);
		for b in recv {
			match b {
				ReaderProduct::FileInfo(filename,size) => {
					out.send(ChunkerProduct::FileInfo(FileInfo::new(chunk_counter,size,filename)));
					chunk_counter = 0;
				}
				ReaderProduct::FileBlock(b) => {
					println!("in file {} block",b.path());
					loop {
						match b.split_rabin_avx8(&self.cond,&mut window,&mut collector,&out) {
							Some((split_time,split_len)) => {
								chunk_counter += 1;
								cost_time += split_time;
								leiji += split_len;
							}
							None => {
								println!("None");
								break;
							}
						}
					}
				}
			}
		}
		println!("leiji {leiji}");
		self.statistics.lock().set_split_phase_time(cost_time);
		Ok(())
	}
}
